package com.okbihuo.perm.modules.auth;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.okbihuo.perm.component.sms.domain.Sms;
import com.okbihuo.perm.component.sms.logic.type.SmsType;
import com.okbihuo.perm.component.sms.service.SmsService;
import com.okbihuo.perm.constants.SystemCode;
import com.okbihuo.perm.constants.YesOrNo;
import com.okbihuo.perm.core.auth.domain.BaseAuthUser;
import com.okbihuo.perm.core.auth.helper.AuthHelper;
import com.okbihuo.perm.core.tool.api.BaseException;
import com.okbihuo.perm.core.tool.api.ResultCode;
import com.okbihuo.perm.core.tool.jackson.JsonUtil;
import com.okbihuo.perm.core.tool.utils.BeanUtil;
import com.okbihuo.perm.core.tool.utils.StringUtil;
import com.okbihuo.perm.mod.user.domain.AuthUser;
import com.okbihuo.perm.mod.user.service.IAuthUserService;
import com.okbihuo.perm.modules.auth.controller.vo.UserInfo;
import com.okbihuo.perm.modules.rwb.product.domain.MyProduct;
import com.okbihuo.perm.modules.rwb.product.domain.Product;
import com.okbihuo.perm.modules.rwb.product.logic.MyProductLogic;
import com.okbihuo.perm.modules.rwb.product.service.MyProductService;
import com.okbihuo.perm.modules.rwb.product.service.ProductService;
import com.okbihuo.perm.modules.rwb.qw.domain.QwCorpAuth;
import com.okbihuo.perm.modules.rwb.qw.domain.RwbUser;
import com.okbihuo.perm.modules.rwb.qw.resultcode.WxSdkRequestErrorCode;
import com.okbihuo.perm.modules.rwb.qw.service.QwCorpAuthService;
import com.okbihuo.perm.modules.rwb.qw.service.RwbUserService;
import com.okbihuo.perm.modules.rwb.sys.domain.RwbSystemSetting;
import com.okbihuo.perm.modules.rwb.sys.service.RwbSystemSettingService;
import com.okbihuo.perm.modules.thirdOpen.merchant.domain.GzhUser;
import com.okbihuo.perm.modules.thirdOpen.merchant.service.GzhUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CachePut;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 安全配置
 */
@Service
@RequiredArgsConstructor
public class UserDetailServiceImpl implements UserDetailsService {

    private final RwbSystemSettingService systemSettingService;
    private final MyProductService myProductService;
    private final ProductService productService;
    private final MyProductLogic myProductLogic;
    private final IAuthUserService authUserService;
    private final QwCorpAuthService corpAuthService;
    private final RwbUserService rwbUserService;
    private final IAuthUserService userService;
    private final SmsService smsService;
    private final GzhUserService gzhUserService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<AuthUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        // 同时有多个不报错，取最后一个
        AuthUser one = authUserService.getOne(queryWrapper, false);
        // 载入角色和权限
        return one;
    }

    /**
     * 手机号获取
     * @param phone
     * @return
     * @throws UsernameNotFoundException
     */
    public AuthUser getByPhone(String phone) throws UsernameNotFoundException {
        QueryWrapper<AuthUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", phone);
        // 同时有多个不报错，取最后一个
        AuthUser one = authUserService.getOne(queryWrapper, false);
        // 载入角色和权限
        return one;
    }

    /**
     * id获取
     * @param id
     * @return
     * @throws UsernameNotFoundException
     */
    @CachePut(value = "AuthUser", key = "#id",unless="#result == null")
    public AuthUser getId(Long id) {
        return userService.getById(id);
    }

    /**
     * 获取当前登陆信息
     * @return
     * @throws BaseException
     */
    public AuthUser currentUser() throws BaseException {
        // 取当前用户
        Optional<BaseAuthUser> baseAuthUser = AuthHelper.currentPrincipal();
        BaseAuthUser current = baseAuthUser.orElseThrow(() -> new BaseException(ResultCode.NOT_FOUND, "当前没有用户"));
        // 获取用户详情
        AuthUser userDetails = (AuthUser)loadUserByUsername(current.getUsername());
        return userDetails;
    }

    /**
     * 获取当前登陆信息
     * @return
     * @throws BaseException
     */
    public UserInfo currentInfo() throws BaseException {
        // 获取用户详情
        AuthUser userDetails = currentUser();
        //获取当前登陆id
        Long loginId = userDetails.getId();
        //获取企微模块id
        RwbUser rwbUser = rwbUserService.getByLoginUserId(loginId);
        //获取公众号模块id
        GzhUser gzhUser = gzhUserService.getByLoginUserId(loginId);
        //获取授权企业
        final QwCorpAuth corpAuth = corpAuthService.getByQwUserId(rwbUser.getId());
        //转换成vo返回
        UserInfo info = BeanUtil.copyProperties(userDetails,UserInfo.class);
        assert info != null;
        info.setQwUserId(rwbUser.getId());
        info.setGzhUserId(gzhUser.getId());
        if(corpAuth != null){
            info.setCorpId(corpAuth.getCorpId());
            info.setAgentId(corpAuth.getAgentId());
        }
        //收集服务产品codes
        final List<Product> products = productService.listSalesProduct();
        final List<String> codes = products.stream().map(Product::getCode).collect(Collectors.toList());
        //获取当前用户的产品
        final List<Map<String, Object>> mapList = myProductLogic.listMapByCodes(userDetails.getId(), codes);
        info.setMyProductList(mapList);
        return info;
    }

    /**
     * 检查当前用户是否为当前账户拥有者
     */
    public boolean checkIsCurrent(Long qwUserId) throws BaseException {
        if(qwUserId == null){
            throw new BaseException(ResultCode.UN_AUTHORIZED,"账户id为空!");
        }
        //当前登陆用户信息
        final UserInfo info = currentInfo();
        //获取企微账户信息
        RwbUser rwbUser = rwbUserService.getById(qwUserId);
        if(rwbUser == null){
            throw new BaseException(new WxSdkRequestErrorCode(), "未获取到当前账户信息");
        }
        if(!info.getQwUserId().equals(rwbUser.getLoginUserId())){
            throw new BaseException(new WxSdkRequestErrorCode(), "账户信息不属于当前登陆用户");
        }
        return true;
    }

    /**
     * 注册
     * @param phone 手机号
     * @param captcha 验证码
     * @param password 密码
     * @throws BaseException
     */
    public void register(String phone,String captcha,String password,String ip) throws BaseException {
        final UserDetails byPhone = getByPhone(phone);
        if(byPhone != null){
            throw new BaseException(ResultCode.FAILURE, "当前手机号已注册");
        }
        Sms sms = smsService.getEffectiveSMS(phone, captcha, SmsType.REGISTER);
        if(sms == null){
            throw new BaseException(ResultCode.DATA_NOT_FOUND, "验证码有误或已失效");
        }
        sms.setAvaliable(YesOrNo.NO.ordinal());
        smsService.saveOrUpdateAndReturn(sms);
        //加密密码sdk
        final PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        //超期时间
        LocalDateTime expired = LocalDateTime.now().plusYears(100);
        //保存用户信息
        AuthUser user = AuthUser.builder()
                .phone(phone)
                .nickname(phone)
                .enabled(true)
                .username(phone)
                .expiresAt(expired)
                .credentialsExpiresAt(expired)
                .password(encoder.encode(password))
                .registryIp(ip)
                .openId(StringUtil.randomUUID())
                .build();
        user = userService.saveOrUpdateAndReturn(user);
        intiAccount(user.getId());
        intiGift(user);
    }

    /**
     * 初始化账户
     * @param userId
     */
    public void intiAccount(Long userId){
        //获取用户之前是否有任务宝账户
        RwbUser user = rwbUserService.getByLoginUserId(userId);
        if(user == null){
            //没有则新建
            rwbUserService.create(userId);
        }

        //获取用户之前是否有任务宝账户
        GzhUser gzhUser = gzhUserService.getByLoginUserId(userId);
        if(gzhUser == null){
            //没有则新建
            gzhUserService.create(userId);
        }
    }


    /**
     * 初始化账户赠送产品信息
     * @param user
     */
    public void intiGift(AuthUser user){
        //获取系统设置的赠送产品信息
        final RwbSystemSetting setting = systemSettingService.getByCode(SystemCode.giftProducts);

        final List<Product> products = JsonUtil.parseArray(setting.getValue(), Product.class);
        List<MyProduct> list = new ArrayList<>();
        for(Product product : products){
           MyProduct myProduct =  MyProduct.builder()
                    .productCode(product.getCode())
                    .productModule(product.getModule())
                    .userId(user.getId())
                    .username(user.getPhone())
                    .userOpenId(user.getOpenId())
                    .endTime(myProductLogic.getEndTime(System.currentTimeMillis(),product.getUnit(),product.getTimes()))
                    .build();
            list.add(myProduct);
        }
        if(list.size() > 0){
            myProductService.saveOrUpdateBatch(list);
        }
    }


    /**
     * 注册
     * @param phone 手机号
     * @param captcha 验证码
     * @param password 密码
     * @throws BaseException
     */
    public void reset(String phone,String captcha,String password) throws BaseException {
        AuthUser byPhone = getByPhone(phone);
        if(byPhone == null){
            throw new BaseException(ResultCode.FAILURE, "当前手机号未注册");
        }
        Sms sms = smsService.getEffectiveSMS(phone, captcha, SmsType.RESET_PASSWORD);
        if(sms == null){
            throw new BaseException(ResultCode.DATA_NOT_FOUND, "验证码有误或已失效");
        }
        sms.setAvaliable(YesOrNo.NO.ordinal());
        smsService.saveOrUpdateAndReturn(sms);
        //加密密码sdk
        final PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        byPhone.setPassword(encoder.encode(password));
        userService.saveOrUpdateAndReturn(byPhone);
    }
}
